package com.heima.schedule.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.common.cache.CacheService;
import com.heima.model.schedule.dto.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.service.TaskService;
import com.heima.schedule.service.TaskinfoLogsService;
import com.heima.schedule.service.TaskinfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskinfoService taskinfoService;

    @Autowired
    private TaskinfoLogsService taskinfoLogsService;

    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Long addTask(Task task) {
        // 0. 判断task是否为空
        if (task == null) {
            log.warn("入参为空");
            return null;
        }
        // 1. 插入taskinfo表
        Taskinfo taskinfo = new Taskinfo();
        BeanUtils.copyProperties(task, taskinfo);
        taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
        boolean taskResult = taskinfoService.save(taskinfo);
        if (!taskResult) {
            log.warn("任务插入失败");
            return null;
        }
        // 2. 插入taskinfolog表
        TaskinfoLogs logs = new TaskinfoLogs();
        BeanUtils.copyProperties(task, logs);
        logs.setExecuteTime(new Date(task.getExecuteTime()));
        logs.setStatus(0);
        boolean logResult = taskinfoLogsService.save(logs);
        if (!logResult) {
            log.warn("日志插入失败");
            throw new RuntimeException();
        }

        task.setTaskId(taskinfo.getTaskId());
        // 3. 判断当前时间与任务执行时间
        long executeTime = task.getExecuteTime();
        long currentTime = System.currentTimeMillis();
        if (executeTime <= currentTime) {
            cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));
        }
        // 4. 判断未来时间和任务执行时间
        long futureTime = currentTime + (5 * 60 * 1000);
        if (executeTime <= futureTime) {
            cacheService.zAdd("FUTURE", JSON.toJSONString(task), executeTime);
        }
        // 5. 返回任务id
        return taskinfo.getTaskId();
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean cancelTask(Long taskId) {
        if (taskId == null) {
            log.warn("入参不能为空");
            return false;
        }
        // 1. 删除taskinfo
        boolean infoResult = taskinfoService.removeById(taskId);
        if (!infoResult) {
            log.warn("任务删除失败");
            return false;
        }
        // 2. 更新taskinfolog
        TaskinfoLogs logs = new TaskinfoLogs();
        logs.setStatus(2);
        boolean logsResult = taskinfoLogsService.updateById(logs);
        if (!logsResult) {
            log.warn("日志删除失败");
            throw new RuntimeException();
        }
        // 3. 组装task对象
        Task task = new Task();
        BeanUtils.copyProperties(logs, task);
        task.setExecuteTime(logs.getExecuteTime().getTime());
        // 4. 删除redis中的task
        cacheService.lRemove("TOPIC", 0, JSON.toJSONString(task));

        cacheService.zRemove("FUTURE", JSON.toJSONString(task));

        return true;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Task pullTask() {
        // 1. 弹出redis中的task
        String taskJSON = cacheService.lRightPop("TOPIC");
        if (StringUtils.isEmpty(taskJSON)) {
            log.warn("task为空");
            return null;
        }
        // 2. 转成task对象
        Task task = JSON.parseObject(taskJSON, Task.class);
        if (task == null) {
            log.warn("没有可执行的任务");
            return null;
        }
        // 2.1 删除taskinfo
        Long taskId = task.getTaskId();
        boolean infoResult = taskinfoService.removeById(taskId);
        if (!infoResult) {
            log.warn("任务删除失败");
            return null;
        }
        // 3. 更新taskinfolog
        TaskinfoLogs logs = new TaskinfoLogs();
        BeanUtils.copyProperties(task,logs);
        logs.setExecuteTime(new Date(task.getExecuteTime()));
        logs.setStatus(1);
        boolean logResult = taskinfoLogsService.updateById(logs);
        if (!logResult) {
            log.warn("日志删除失败");
            throw new RuntimeException("日志删除失败");
        }

        return task;
    }

    @Scheduled(fixedRate = 1000 * 5 * 60)
    public void refresh() {
        log.info("执行了");
        String lockToken = cacheService.tryLock("refresh_lock", 5000);
        if (StringUtils.isEmpty(lockToken)) {
            return;
        }
        Set<String> tasks = cacheService.zRangeByScore("FUTURE", 0, System.currentTimeMillis());
        if (CollectionUtils.isEmpty(tasks)) {
            return;
        }
        cacheService.refreshWithPipeline("FUTURE", "TOPIC", tasks);
    }

    @Scheduled(fixedRate = 1000 * 5 * 60)
    public void initTask() {
        clear();

        List<Taskinfo> taskInfoList = taskinfoService.list();
        if (!CollectionUtils.isEmpty(taskInfoList)) {
            for (Taskinfo taskinfo : taskInfoList) {
                if (taskinfo == null) {
                    continue;
                }
                long executeTime = taskinfo.getExecuteTime().getTime();
                long currentTime = System.currentTimeMillis();
                long futureTime = currentTime + (5 * 60 * 1000);

                Task task = new Task();
                BeanUtils.copyProperties(taskinfo, task);
                task.setExecuteTime(executeTime);

                if (executeTime <= currentTime) {
                    cacheService.lLeftPush("TOPIC",JSON.toJSONString(task));
                } else if (executeTime <= futureTime) {
                    cacheService.zAdd("FUTURE",JSON.toJSONString(task),currentTime);
                }
            }
        }

    }

    private void clear() {
        cacheService.delete("TOPIC");
        cacheService.delete("FUTURE");
    }
}
